;;; Code:
(require 'dired-x)
(require 'image-dired)
(require 'dired-hacks-utils)
(require 'dired-subtree)
(require 'dired-narrow)
(require 'dired-imenu)

;; Faces
(defface distopico:diredp-executable-file-name
  '((t (:foreground "Red" :weight bold)))
  "*Face used for names of executable files in `dired' buffers."
  :group 'dired :group 'font-lock-highlighting-faces)
(defvar distopico:diredp-executable-file-name 'distopico:diredp-executable-file-name)

;; Autoload
(autoload 'dired-async-mode "dired-async.el" nil t)

;; Config
(setq dired-find-subdir t
      dired-dwim-target t ;; Move files between split panes
      dired-omit-verbose nil
      dired-kill-when-opening-new-dired-buffer t ;; Reuse same buffer
      dired-hide-details-hide-symlink-targets nil
      dired-omit-files "^\\..*[a-zA-Z]"
      dired-listing-switches (purecopy "-al")
      image-dired-thumbnail-storage 'standard
      image-dired-main-image-directory "~/Pictures"
      image-dired-db-file (in-emacs-d ".cache/image-dired/image-dired_db")
      image-dired-temp-image-file (in-emacs-d ".cache/image-dired/image-dired_temp")
      image-dired-gallery-dir (in-emacs-d ".cache/image-dired/image-dired_gallery"))

;; Custom font faces
(add-to-list 'dired-font-lock-keywords (list dired-re-exe
					     `(".+"
					       (dired-move-to-filename)
					       nil
					       (0 distopico:diredp-executable-file-name t))))

;; Async mode
(dired-async-mode +1)

;; Custom keymap
(define-key dired-mode-map (kbd "C-<right>") 'dired-subtree-insert)
(define-key dired-mode-map (kbd "C-<left>") 'dired-subtree-remove)
(define-key dired-mode-map (kbd "_") 'dired-up-directory)
(define-key dired-mode-map (kbd "|") 'dired-up-directory)
(define-key dired-mode-map (kbd "M--") 'dired-up-directory)
(define-key dired-mode-map (kbd "C-c s") 'distopico:dired-sort)
(define-key dired-mode-map (kbd "r") 'distopico:dired-toggle-reverse)
(define-key dired-mode-map [mouse-2] 'dired-mouse-find-file)
(define-key dired-mode-map [mouse-3] 'dired-maybe-insert-subdir)
(define-key dired-mode-map (kbd "C-{") 'distopico:dired-narrow-window)
(define-key dired-mode-map "e" 'distopico:dired-ediff-files)
(define-key dired-mode-map (vector 'remap 'end-of-buffer) 'distopico:dired-jump-to-bottom)
(define-key dired-mode-map (vector 'remap 'beginning-of-buffer) 'distopico:dired-back-to-top)
;; TODO: change this with own functionality
(define-key image-dired-thumbnail-mode-map (kbd "C-n") 'image-diredx-next-line)
(define-key image-dired-thumbnail-mode-map (kbd "C-p") 'image-diredx-previous-line)

;; Functions
(defun distopico:dired-mode-hook ()
  "Enable modes in dired."
  (visual-line-mode -1) ;; unwrap lines.
  (linum-mode -1) ;; turn off line numbers.
  (hl-line-mode +1) ;; hl-line - highlight current-line
  (auto-revert-mode +1) ;; auto-refresh dired
  (font-lock-mode +1) ;; Switch-on font-lock
  (dired-omit-mode +1) ;; Set omit-mode by default
  (dired-hide-details-mode +1))

(defun distopico:dired-after-readin-hook ()
  "Set custom name to `dired' buffers."
  (rename-buffer (format "dired:%s" (generate-new-buffer-name dired-directory))))

(defun distopico:dired-back-to-top ()
  "Make `end-of-buffer' and `beginning-of-buffer' behave properly."
  (interactive)
  (goto-char (point-min))
  (forward-line 2))

(defun distopico:dired-jump-to-bottom ()
  "Jump to button of buffer."
  (interactive)
  (goto-char (point-max))
  (forward-line -1))

(defun distopico:dired-next-file-line ()
  "Move to the next `dired' line that have a file or directory name on it."
  (interactive)
  (call-interactively 'dired-next-line)
  (if (eobp)
      (dired-previous-line 1)))

(defun distopico:dired-previous-file-line ()
  "Move to the previous `dired' line that have a file or directory name on it."
  (interactive)
  (call-interactively 'dired-previous-line)
  (if (not (dired-move-to-filename))
      (dired-next-line 1)))

(defun distopico:dired-sort ()
  "Sort `dired' by different options such as date,size etc."
  (interactive)
  (when dired-sort-inhibit
    (error "Cannot sort this Dired buffer"))
  (let* ((sort-by (completing-read
		  "Sort by:"
		  '("size"
		    "name"
		    "a-z"
		    "date"
		    "ctime"
		    "utime"
		    "extension"
		    "dir")))
	(sort-arg (cond
		   ((equal sort-by "size") "Sh")
		   ((equal sort-by "name") "")
		   ((equal sort-by "a-z") "U")
		   ((equal sort-by "date") "t")
		   ((equal sort-by "ctime") "ct")
		   ((equal sort-by "utime") "ut")
		   ((equal sort-by "extension") "AX")
		   ((equal sort-by "dir")  "Apv")
		   (t ""))))
    (dired-sort-other (concat dired-listing-switches sort-arg))))

(defun distopico:dired-toggle-reverse ()
  "Toggle `dired' reverse order using the existing `dired-actual-switches'."
  (interactive)
  (when dired-sort-inhibit
    (error "Cannot sort this Dired buffer"))
  (if (string-match "\\(-r\\)\\(\s\\|$\\)" dired-actual-switches)
      (dired-sort-other (string-trim (replace-match "" t t dired-actual-switches)))
  (dired-sort-other (string-trim (concat dired-actual-switches " -r")))))

(defun distopico:dired-ediff-files ()
  "Ediff two marked files.
from: https://oremacs.com/."
  (interactive)
  (let ((files (dired-get-marked-files))
        (wnd (current-window-configuration)))
    (if (<= (length files) 2)
        (let ((file1 (car files))
              (file2 (if (cdr files)
                         (cadr files)
                       (read-file-name
                        "file: "
                        (dired-dwim-target-directory)))))
          (if (file-newer-than-file-p file1 file2)
              (ediff-files file2 file1)
            (ediff-files file1 file2))
          (add-hook 'ediff-after-quit-hook-internal
                    (lambda ()
                      (setq ediff-after-quit-hook-internal nil)
                      (set-window-configuration wnd))))
      (error "No more than 2 files should be marked"))))


;; Advice functions
(defun distopico:dired-insert-directory (orig-fun &rest args)
  "Change `dired-insert-directory' to list directories first.
Add '--group-directories-first' to `SWITCHES' from `ORIG-FUN'
the original function `ARGS'.
see `dired-insert-directory' documentation for more information."
  (let ((switches (nth 1 args)))
    (setf (nth 1 args) (concat switches " --group-directories-first"))
    (apply orig-fun args)))

(advice-add 'dired-insert-directory :around #'distopico:dired-insert-directory)

;; TODO: check if those still necessary
;; Reload dired after making changes
;; (--each '(dired-do-rename
;;           dired-do-copy
;;           dired-create-directory
;;           wdired-abort-changes)
;;   (eval `(defadvice ,it (after revert-buffer activate)
;;            (revert-buffer))))


;; Hooks
(add-hook 'dired-mode-hook 'distopico:dired-mode-hook)
(add-hook 'dired-after-readin-hook 'distopico:dired-after-readin-hook)
;; Attach files with C-c RET C-a
(add-hook 'dired-mode-hook 'turn-on-gnus-dired-mode)

(provide 'conf-dired)
;;; conf-dired.el ends here
